#!/usr/bin/perl
use strict;
use vars qw($OS_win);
#Copyright Donomi, 2003-
#GPL v3 or greater.  Love it.

=head1 NMEA

NMEA - Read latitude, etc from a GPS device

=head1 ABSTRACT

nmea reads data from a device that supports the nmea protocol.  The nmea is a common protocol used by GPS receivers and other maritime devices like autopilots.

This might work with any GPS if it is in the right mode ( NMEA mode ), however it was specifically written for the Sigtec MG5000 series GPS receivers.

=head1 Example

  nmea --port /dev/ttyS0 --verbose --latitude --longitude

  nmea --port /dev/usb/tty/0 --verbose --latitude --longitude

=head1 Options

=item --v, --verbose, --nov, --noverbose

Switch human friendly output on or off.  Verbose is on by default.

=item --m, --mode

The query you want to use on the device.  The options are position, satellite, date and daemon.  Position, satellite and date return some values and then quit.  daemon mode causes puts the GPS into report mode and writes the lat, long, etc into files, and logs the position of the GPS once a second into a file.

This is the mode to use with any of the other entirety programs

Example:

  perl nmea.pl --mode=daemon

=item --ti, --time

Add a timestamp to the output.

=item --la, --latitude

Get the current latitude

=item --lo, --longitude

Get the current longitude

=item --a, --altitude

Get the current altitude

=item --q, --quality

The quality of the reading, and the number of satellites currently being used.

=item --p, --port

The full path to the serial device the GPS is attached to.

Defaults to /dev/ttyS0
You might also try /dev/usb/tty/0 or COM2

Example

  nmea --port=/dev/usb/tty/0

=item --b, --baud

The baud rate, in bytes per sec, that we should attempt to communicate at.

Defaults to 9600

=item --c, --config

The full path to a config file holding the configuration details that you want nmea to use

Example
  nmea --config /mnt/directory/alternative_config

=cut

use strict;
use Getopt::Long;
$|++;
my ($latitude, $longitude, $altitude, $verbose, $quality, $time, $date, $speed, $heading, $knots, $config_path) = 0;
my %current;
$verbose=1;
#my $output= `cat /etc/entirety/device_root`;
my $output = '';

my $baud = 9600;
my $mode = "position";
my $port;
$mode="daemon";
my $result = GetOptions (
													"time!"		=>	\$time,
													"latitude!"		=>	\$latitude,
													"longitude!"	=>	\$longitude,
													"altitude!"		=>	\$altitude,
													"verbose!"		=>	\$verbose,
													"quality!"		=>	\$quality,
													"speed!"			=>	\$speed,
													"heading!"		=>	\$heading,
													"port:s"			=>	\$port,
													"mode:s"			=>	\$mode,
													"baud=i"			=>	\$baud,
													"config:s"		=>	\$config_path,
												);
my $hdir = $ENV{HOME};

if ( !defined($hdir) || $hdir eq "" ) 
	{ $hdir = $ENV{HOMESHARE};}
if ( !defined($hdir) || $hdir eq "" ) 
	{ $hdir = $ENV{HOMEDRIVE}.$ENV{HOMEPATH};}

if ( !defined($hdir) || $hdir eq "" ) 
	{ 
		if ( $^O =~ /mswin/i )
			{ $hdir = "c:/tmp";}
		else
			{ $hdir = "/tmp";}
	}
if ( defined($hdir) )
	{ 
		if ( $^O =~ /mswin/i )
			{ $hdir .= "\\My Documents\\My Maps\\"; }
		#else
		#	{ $hdir .= "/.gps_files/";}
	}
 
		if ( $^O =~ /mswin/i )
			{ $output = $hdir."\\GPS files"; }
		else
			{ $output = $hdir."/.gps_files/";}

make_gps_dirs();

my $fname = $config_path;
if ( !defined($config_path) || $config_path eq "" )
	{ 
		if ( ( $^O =~ /mswin/i) )
			{ $fname = $hdir.'Settings';}
		else
			{ $fname = $hdir.'/.entiretyrc';}
		print "Using config file: $fname \n";
	}
open FH, "<", $fname or die "Couldn't open config file $fname";
my $log_dir = $hdir.'/.Entirety_Logs';
if ( ($^O eq 'cygwin') || ( $^O =~ /mswin/i) )
	{ $log_dir = $hdir.'/Entirety_Logs';}
while ( <FH> )
	{
		my ( $key, $val ) = split /=/;
		$key =~ s/\s|"//g;
		$val =~ s/\s|"//g;
		if ( $key =~ /Entirety_Logs/ )
			{$log_dir = $val; chomp $log_dir}
		if ( $key =~ /Serial_Port/ )
			{if ( $port eq "" ) { $port = $val; chomp $port}}

	}

if ($port eq "") { $port = "/dev/ttyS0" }

mkdir $log_dir;
use Data::Dumper;
BEGIN {
        $OS_win = ($^O eq "MSWin32") ? 1 : 0;

        print "Perl version: $]\n";
        print "OS   version: $^O\n";

            # This must be in a BEGIN in order for the 'use' to be conditional
        if ($OS_win) {
            print "Loading Windows module\n";
            eval "use Win32::SerialPort";
	    die "$@\n" if ($@);

        }
        else {
            print "Loading Unix module\n";
            eval "use Device::SerialPort";
	    die "$@\n" if ($@);
        }
} # End BEGIN
sub dp;
my $PortName = $port;
my ($quiet, $lockfile);
my $PortObj;


open_port();

if ( $mode eq 'daemon' )
	{
		my $t = time();
		while(1)
			{
				#if (time()-$t>1)
					#{
						#system ('rm -rf /tmp/gps/*');}
						$t = time();	
						print "Awaiting message..." if $verbose;
						my $message = read_message();
						if ( $message =~ /GPG/ ) { print "Received position reading\n" if $verbose;}
						if ( $message =~ /VTG/ ) { print "Received velocity reading\n" if $verbose;}
						if ( $message =~ /SNC/ ) { print "Received satellite strength reading\n" if $verbose;}
						#print "Processing message:\n" if $verbose;
						daemon_process($message);
					#}
			}

foreach ( $mode )
	{
		/position/ 	&& 	process();
		/speed/ 	&& 	process();
		#/time/ 	&& 	process();
		/satellite/ &&	process() && process();  #we only get half the satellites
	}

sub process
	{
		my $message = read_message();
		#print "\nRead message: $message\n" if $verbose;
		unless ( $message )
			{
				print STDERR "Unable to get reading from GPS\n";
				exit;
			}
  		#if ( $message =~ /\*([0-9A-F][0-9A-F])$/ ) 
		#	{
		#		my ( $csum ) =  "0x".$1 ;
		#		for my $c ( split( // )) 
		#			{ $csum ^= ord( $c ); }
		#
		#		if ( $csum ) 
		#			{
		#				print "Invalid checksum!\n";
		#		  		next; # invalid checksum
		#			}
  		#	 }

		foreach ( $mode )
			{
				/position/ 	&& 	position($message);
				#/time/ 	&& 	do_time($message);
				/speed/ 	&& 	speed($message);
				/satellite/ &&	zero_all_channels() && sat_report($message);  #we only get half the satellites
			}
		1;
	}

sub dp
	{
		my $fname = shift;
		my @content = @_;
		open FH, ">", $fname or die "Couldn't open $fname";
		print FH @content;
		close FH;
	}

{
my $last_zeroed_time;
sub zero_all_channels
	{
  if ( ( time() - $last_zeroed_time ) < 2 )
  	{ return; }

  $last_zeroed_time = time();

  my %sync=
                                                        (
                                                                V       =>      'no',
                                                                B       =>      'bit',
                                                                W       =>      'word',
                                                                S       =>      'subframe',
                                                                T       =>      'time of week',
                                                                E       =>      'ephemeris',
                                                        );
                                                my %tracking =
                                                        (
                                                                t       =>      'tracking',
                                                                r       =>      'reacquisition',
                                                                a       =>      'acquisition',
                                                        );
		my $sync = 'V';
		my $track= 'a';
		my $strength=0;
		for my $cnum ( 0..32 )
			{
				my $channel = sprintf "%.2d", $cnum;
				if ( $channel == 0) { $channel = '0';}
				mkdir $output."/channels/$channel";
				mkdir $output."/raw/channels/$channel";
				open FH, ">", $output."/channels/$channel/strength" or warn "Couldn't open channel file: $output/channels/$channel/strength";
				print FH $strength;
                                close FH;

                                open FH, ">", $output."/raw/channels/$channel/strength" or warn "Couldn't open channel file: $output/raw/channels/$channel/strength";
                                print FH pack "c",$strength;
                                close FH;

                                open FH, ">", $output."/channels/$channel/mode" or warn "Couldn't open channel file: $output/channels/$channel/mode";
                                print FH $tracking{$track};
                                close FH;

                                open FH, ">", $output."/raw/channels/$channel/mode" or warn "Couldn't open channel file: $output/raw/channels/$channel/mode";
                                print FH $track;
                                close FH;

                                open FH, ">", $output."/channels/$channel/sync" or warn "Couldn't open channel file: $output/channels/$channel/sync";
                                print FH $sync{$sync};
                                close FH;

                                open FH, ">", $output."/raw/channels/$channel/sync" or warn "Couldn't open channel file: $output/raw/channels/$channel/sync";
                                print FH $sync;
                                close FH;
			}
		1;
	}
}


sub daemon_process
	{
		my $message = shift;
		#print "\n->|$message|<-\n" if $verbose;
  		if ( $message =~ /\*([0-9A-F][0-9A-F])$/ ) 
			{
				my $csum = "0x".$1;
				for my $c ( split( // )) 
					{
	  					$csum ^= ord( $c );
					}
	
				if ( $csum ) 
					{
						print "Invalid checksum!\n";
  						return;
					}
  	 		}

		foreach ( $message )
			{
				/GGA/ 	&& 	do
					{
						my @params = split /,/, $message;
						foreach my $i ( 2,4 )
							{
								$params[$i] =~ s/(...?)(..)\.(.*)/$1 $2\.$3/;
							}
						#Latitude
						dp $output."/latitude",$params[2], " ", $params[3];

						#Latitude in minutes
						my ($hours, $minutes) = split / /, $params[2];
						$minutes += $hours*60;
						if ( $params[3] =~ /S/i )
							{
								$minutes *= -1;
							}
						dp $output."/latitude_mins", $minutes;
						$current{latitude} = $minutes;

						#Binary latitude
						my ($hours, $minutes) = split / /, $params[2];
						$minutes += $hours*60;
						if ( $params[3] =~ /S/i )
							{
								$minutes *= -1;
							}
						dp $output."/raw/latitude", pack( "d",$minutes);

						#Longitude
						dp $output."/longitude", $params[4], " ", $params[5];

						#Longitude in minutes
						my ($long_hours, $long_minutes) = split / /, $params[4];
						$long_minutes += $long_hours*60;
						if ( $params[5] =~ /W/i )
							{
								$long_minutes *= -1;
							}
						dp $output."/longitude_mins", $long_minutes;
						$current{longitude} = $long_minutes;

						#Binary longitude in minutes
						my ($long_hours, $long_minutes) = split / /, $params[4];
						$long_minutes += $long_hours*60;
						if ( $params[5] =~ /W/i )
							{
								$long_minutes *= -1;
							}
						dp $output."/raw/longitude", pack ("d", $long_minutes);

						#Altitude
						dp $output."/altitude", $params[9];
						$current{altitude} = $params[9];

						#Binary altitude
						dp $output."/raw/altitude", pack( "d", $params[9]);

						#Number of satellites used for reading
						dp $output."/satellites", $params[7];
						$current{number_of_satellites} = $params[7];

						#Make an almost-unique filename from the computer name and the date
						my $hostname = `hostname`;
						chomp $hostname;
						my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
						my $date =  ($year+1900)."_".$mon."_".$mday;
						my $name = $hostname."_".$date;
						print "\nLogging to $name\n" if $verbose;
						#Put them all together for the log
						open FH, ">>", $log_dir.'/'.$name;
						my ($lat_deg, $lat_min) = split / /, $params[2];
						my $xmllat = $lat_deg + $lat_min/60;
						my ($lon_deg, $lon_min) = split / /, $params[4];
						my $xmllon = $lon_deg + $lon_min/60;
						if ( $params[5] =~ /W/i ) { $xmllon *= -1; }
						if ( $params[3] =~ /S/i ) { $xmllat *= -1; }
						my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
						     my $iso_time =  ($year+1900)."-".$mon."-".$mday."T".$hour.":".$min.":".$sec."Z";
						print FH '<wpt lat="'.$xmllat.'" lon="'.$xmllon.'">', "\n";
						print FH '<ele>'.$params[9].'</ele>', "\n";
						print FH '<time>'.$iso_time.'</time>', "\n";
						print FH '<sym>Dot</sym>', "\n";
						print FH '<type>Dot</type>', "\n";
						print FH '<hdop>'.$params[8].'</hdop>'."\n";
						print FH '<sat>'.$current{number_of_satellites}.'</sat>'."\n";
						print FH '<extensions>\n';
						print FH '<speed>'.$current{kmph}.'</speed>'."\n";
						print FH '<heading>'.$current{heading}.'</heading>'."\n";
						print FH '</extensions>'."\n";
						print FH '</wpt>', "\n";
						#print FH time, ",", $minutes, ",", $long_minutes, ",", $params[9], ",", $current{heading}, ",", $current{kmph}, "\n";
						close FH;
					};
				/VTG/ 	&& 	do
					{
						my @params = split /,/, $message;
						#heading
						dp $output."/heading", $params[1];
						$current{heading} = $params[1];

						#Binary heading
						dp $output."/raw/heading", pack ("d", $params[1]);
						
						#kmph
						dp $output."/kmph",$params[7];
						$current{kmph} = $params[7];

						#Binary kmph
						dp $output."/raw/kmph", pack ("d", $params[7]);

						#Knots
						dp $output."/knots", $params[5];
					};
				/SNC/ &&	do
					{
						my @params = split /,/, $message;
						#print "Params are  @params\n" if $verbose;
						shift @params for 1..4;
						my %sync=
							(
								V	=>	'no',
								B	=>	'bit',
								W	=>	'word',
								S	=>	'subframe',
								T	=>	'time of week',
								E	=>	'ephemeris',
							);
						my %tracking =
							(
								t	=>	'tracking',
								r	=>	'reacquisition',
								a	=>	'acquisition',
							);
						#Clear all the gps strength readings on the disk
						while ( @params )
							{
								my $channel = shift @params;
								my $strength = shift @params;
								mkdir $output."/channels/";
								mkdir $output."/raw/channels/";
								mkdir $output."/channels/$channel/";
								mkdir $output."/raw/channels/$channel/";
								open FH, ">", $output."/channels/$channel/strength" or die "Couldn't open channel file: $output/channels/$channel/strength\n";
								print FH $strength;
								close FH;
								open FH, ">", $output."/raw/channels/$channel/strength" or die "Couldn't open channel file: $output/raw/channels/$channel/strength\n";
								print FH pack "c",$strength;
								close FH;
								my ( $sync, $track ) = split //, shift @params;
								open FH, ">", $output."/channels/$channel/mode" or die "Couldn't open channel file: $output/channels/$channel/mode\n";
								print FH $tracking{$track};
								close FH;
								open FH, ">", $output."/raw/channels/$channel/mode" or die "Couldn't open channel file: $output/raw/channels/$channel/mode\n";
								print FH $track;
								close FH;
								open FH, ">", $output."/channels/$channel/sync" or die "Couldn't open channel file: $output/channels/$channel/sync\n";
								print FH $sync{$sync};
								close FH;
								open FH, ">", $output."/raw/channels/$channel/sync" or die "Couldn't open channel file: $output/raw/channels/$channel/sync\n";
								print FH $sync;
								close FH;

							}

					};
			}
		1;
	}
sub send_message
	{
		my $mode = shift;
		if ( $mode eq "daemon" )
			{
				my $count_out = $PortObj->write('$GPGPQ,GGA,1'."\r\n") || warn "Error writing to port: $!\n";
				print "Sent request for GGA reports - sent $count_out bytes\n";
				my $count_out = $PortObj->write('$GPGPQ,VTG,1'."\r\n") || warn "Error writing to port: $!\n";
				print "Sent request for VTG reports - sent $count_out bytes\n";
                                my $count_out = $PortObj->write('$GPGPQ,SNC,1'."\r\n") || warn "Error writing to port: $!\n";
                                print "Sent request for SNC reports - sent $count_out bytes\n";
                                my $count_out = $PortObj->write('$GPGPQ,GSA,1'."\r\n") || warn "Error writing to port: $!\n";
                                print "Sent request for GSA reports - sent $count_out bytes\n";

				print "Daemon mode set\n";
			}
		else
			{
		my %mode = ( 
			position 	=>	'$GPGPQ,GGA'."\r\n",
			speed 	=>	'$GPGPQ,VTG'."\r\n",
			time 	=>	'$GPGPQ,PPS'."\r\n",
			satellite	=>	'$GPGPQ,SNC'."\r\n",
			);
		my $output_string = $mode{$mode};
		my $count_out = $PortObj->write($output_string);
		print "Sent request for $mode{$mode} - sent $count_out bytes\n";
	}
	}

sub read_message
	{
		my $InBytes = 1;
		my ($c, $string);
		my $start_time = time;
		#while ( !($c =~ /\n$/) )
			#{
				#$string .= $c;
				#my $count_in;
		  		#($count_in, $c) = $PortObj->read($InBytes);
			  		##warn "read unsuccessful\n" unless ($count_in == $InBytes);
				#last if (time-$start_time>2);
			#}
		my $gotit = "";
		$PortObj->are_match("\n");
require Time::HiRes; 
import Time::HiRes 'usleep';
		until ( "" ne $gotit )
			{
				usleep (1);
				#print "." if $verbose;
				$gotit=$PortObj->lookfor;
				if ( time() - $start_time > 5) 
					{ 
						if ( $mode eq "daemon" )
							{
								print"\nGPS not responding.  Attempting to reconnect.\n"; 
								open_port();
								$start_time=time;
							}
						else
						{ print "\nUnable to talk to GPS.  Now quitting\n";exit(1)}
					}
			}
		#print $gotit;
		#print "\n" if $verbose;
		return $gotit;
		return $string;
	}

sub speed
	{
		my $string = shift;
		my @params = split /,/, $string;
		if ( $heading )
			{
				print "Heading: " if $verbose;
				print $params[1], "\n";
			}
		if ( $knots )
			{
				print "Knots: " if $verbose;
				print $params[5], "\n";
			}
		if ( $speed )
			{
				print "kmph: " if $verbose;
				print $params[7], "\n";
			}
	}

sub do_time
	{
		my $string = shift;
		my @params = split /,/, $string;
		my ($hours, $minutes, $seconds) = ( $params[0] =~ /(..)(..)(.*)/);
		if ( $time )
			{
				print $hours, ":", $minutes, ":", $seconds, "\n";
			}
		if ( $date )
			{
				print $params[1], "/", $params[2], "/", $params[3], "\n";
			}
	}
	
sub position
	{
		my $string = shift;
		my @params = split /,/, $string;
		foreach my $i ( 2,4 )
			{
				$params[$i] =~ s/(...?)(..)\.(.*)/$1 $2\.$3/;
		}
		$time && do
			{
				$verbose && print "Time: ";
				print time, "\n";
			};
		$latitude && do 
			{
				$verbose && print "Latitude: ";
				print $params[2], " ", $params[3], "\n";
			};
		$longitude && do
			{
				$verbose && print "Longitude: ";
				print $params[4], " ", $params[5], "\n";
			};
		$altitude && do
			{
				$verbose && print "Altitude: ";
				print $params[9], "\n";
			};
		$quality && do 
			{
				$verbose && print "Reading quality is ";
				print $params[6];
				print $verbose?" based on ":"\n";
				print $params[7];
				$verbose && print " satellites\n";
			};
		#print "Last DGPS update: ", ($params[13]?$params[13]:"never"), "\n";
		
}


sub print_help
	{
		#pod2txt here
	}

sub sat_report
	{
		my $string = shift;
		my @params = split /,/, $string;
		shift @params for 1..4;
		my %sync=
			(
				V	=>	'no',
				B	=>	'bit',
				W	=>	'word',
				S	=>	'subframe',
				T	=>	'time of week',
				E	=>	'ephemeris',
			);
		my %tracking =
			(
				t	=>	'tracking',
				r	=>	'reacquisition',
				a	=>	'acquisition',
			);
		while ( @params )
			{
				my $channel = shift @params;
				my $strength = shift @params;
				if ($verbose)
					{
						my ( $sync, $track ) = split //, shift @params;
						print "Channel $channel is in ", $tracking{$track}, " mode, and has ", $sync{$sync}, " sync, with strength $strength\n";
					}
				else
					{
						print $channel, "\n";
						print $strength, "\n";
						print shift @params, "\n";
					};
			}
		return 1;
	}


sub print_help
	{
		#pod2txt here
	}

sub open_port
	{
		eval { undef $PortObj; };
		print "Reconnecting to GPS on $PortName...";
		#zero_all_channels();
		my $time_latch = time();
		while (  ! $PortObj )
			{
				
				my $quiet = 1;
				$OS_win = ($^O eq "MSWin32") ? 1 : 0;
			        if ($OS_win) 
						{
							eval {$PortObj = Win32::SerialPort->new ($PortName, 1);};
							    die "$@\n" if ($@);
							if ( $! =~ /No such file or directory/ )
								{ print "\n\nThe serial port you have named ($port) does not seem to exist.  Perhaps you have a USB GPS and it has been allocated the wrong port?\n\nTry different ports with the --port=COM1 command line option, or edit the Settings file in My Maps in your My Documents folder\n"; }

        					}
			        else 
						{
							eval {$PortObj = new Device::SerialPort ($PortName, $quiet, $lockfile);};
							    die "$@\n" if ($@);
					        }


				if ( $PortObj == undef()  )
					{ 
						if ( (time() - $time_latch) > 2 )
							{
								$time_latch = time();
								print "Unable to contact GPS.  Please check you have plugged it into the correct port($PortName)\n";
							}
					}
				#eval {$PortObj = new Device::SerialPort ($PortName, 1, $lockfile);};
				print ".";
			}
		print "Done\n";
		#$PortObj->error_msg(1);
		#$PortObj->user_msg(1);

		print "Setting 8,1,n and baudrate of $baud...";

		$PortObj->databits(8);
		$PortObj->stopbits(1);
		$PortObj->parity('none');

		$PortObj->handshake('none');
		$PortObj->baudrate($baud);
		$PortObj->buffers(4096, 4096);
		print "done\n";
		

		$PortObj->write_settings || warn "Write settings to port failed\n";
		my ( $BlockingFlags) = $PortObj->status || warn "Could not get status\n";
		if ($BlockingFlags) {warn "Port is blocked\n";}

		print "Cancelling GGA reports...";
                my $count_out = $PortObj->write('$GPGPQ,GGA,-1'."\r\n") || warn "Error writing to port: $!\n";;
		print "sent $count_out bytes\n";
                my $count_out = $PortObj->write('$GPGPQ,VTG,-1'."\r\n") || warn "Error writing to port: $!\n";
		print "Cancelling VTG reports - sent $count_out bytes\n";
                my $count_out = $PortObj->write('$GPGPQ,SNC,-1'."\r\n") || warn "Error writing to port: $!\n";
		print "Cancelling SNC reports - sent $count_out bytes\n";
		
		sleep 2;
		print "Setting gps reports for mode $mode\n";
		send_message($mode);
	}
}
 
sub make_gps_dirs 
	{
		chomp $output;
mkdir $output;
mkdir $output."/raw/";
open FH, ">", $output."readme.txt";
print FH "A brief description of the files and the information they contain.

latitude_mins
	The latitude in decimal minutes, with the hours converted into minutesand added on.  This probably sounds a little weird, but is the only sane way to do calculations, unless you are the kind of person who finds it fun to do arithmetic using roman numerals. If the reading is in the southern hemisphere, the number will be negative.
latitude
	The reading in the traditional format, but with decimal minutes instead of minutes and seconds.
longtitude_mins
	The latitude in decimal minutes, with the hours converted into minutesand added on.  This probably sounds a little weird, but is the only sane way to do calculations, unless you are the kind of person who finds it fun to do arithmetic using roman numerals. If the reading is in the western hemisphere, the number will be negative.
longitude
	The reading in the traditional format, but with decimal minutes instead of minutes and seconds.
altitude
	The height above the geodal sphere, in meters.
kmph
	Speed, in kmph
knots
	Speed, in knots
heading
	Heading, in degrees
number_of_satellites
	The number of satellites used to calculate the reading.
valid
	For a number of reasons, the reading my be invalid.  The most likely reason is that the GPS may not have sufficient satellites, and so is using the last reading.  If the reading is valid, the file will contain the word valid, otherwise, nothing.
HDOP
	Horizontal diolution of precision.

raw
	Some of the above files are duplicated in the 'raw' directory.  The contents of these files are similar to the files in the gps directory, but they are in byte format, to be read by C programs.  The format is double precision native format, except where noted.
latitude
	latitude in minutes
longitude
	longitude in minutes
altitude
	altitude in minutes
";
 
}
